#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdarg.h>
#include <assert.h>
#include <ctype.h>
#include "ILexer.h"
#include "Scintilla.h"
#include "SciLexer.h"
#include "WordList.h"
#include "LexAccessor.h"
#include "Accessor.h"
#include "StyleContext.h"
#include "CharacterSet.h"
#include "LexerModule.h"

#ifdef SCI_NAMESPACE
using namespace Scintilla;
#endif

static void ColouriseConfDoc( unsigned int startPos, int length, int, WordList *keywordLists[], Accessor &styler ) {
  int state = SCE_CONF_DEFAULT;
  char chNext = styler[startPos];
  int lengthDoc = startPos + length;
  char *buffer = new char[length + 1];
  int bufferCount = 0;
  WordList &directives = *keywordLists[0];
  WordList &params = *keywordLists[1];
  styler.StartAt( startPos );
  styler.StartSegment( startPos );
  for( int i = startPos; i < lengthDoc; i++ ) {
    char ch = chNext;
    chNext = styler.SafeGetCharAt( i + 1 );
    if( styler.IsLeadByte( ch ) ) {
      chNext = styler.SafeGetCharAt( i + 2 );
      i++;
      continue;
    }
    switch( state ) {
      case SCE_CONF_DEFAULT:
        if( ch == '\n' || ch == '\r' || ch == '\t' || ch == ' ' ) {
          styler.ColourTo( i, SCE_CONF_DEFAULT );
          break;
        } else if( ch == '#' ) {
          state = SCE_CONF_COMMENT;
          styler.ColourTo( i, SCE_CONF_COMMENT );
        } else if( ch == '.' ) {
          state = SCE_CONF_EXTENSION;
          styler.ColourTo( i, SCE_CONF_EXTENSION );
        } else if( ch == '"' ) {
          state = SCE_CONF_STRING;
          styler.ColourTo( i, SCE_CONF_STRING );
        } else if( IsASCII( ch ) && ispunct( ch ) ) {
          styler.ColourTo( i, SCE_CONF_OPERATOR );
        } else if( IsASCII( ch ) && isalpha( ch ) ) {
          bufferCount = 0;
          buffer[bufferCount++] = static_cast<char>( tolower( ch ) );
          state = SCE_CONF_IDENTIFIER;
        } else if( IsASCII( ch ) && isdigit( ch ) ) {
          bufferCount = 0;
          buffer[bufferCount++] = ch;
          state = SCE_CONF_NUMBER;
        } else
        { styler.ColourTo( i, SCE_CONF_DEFAULT ); }
        break;
      case SCE_CONF_COMMENT:
        if( ch == '\n' || ch == '\r' ) {
          state = SCE_CONF_DEFAULT;
        } else
        { styler.ColourTo( i, SCE_CONF_COMMENT ); }
        break;
      case SCE_CONF_EXTENSION:
        if( ( IsASCII( ch ) && isalnum( ch ) ) || ( ch == '_' ) ||
            ( ch == '-' ) || ( ch == '$' ) ||
            ( ch == '/' ) || ( ch == '.' ) || ( ch == '*' ) ) {
          styler.ColourTo( i, SCE_CONF_EXTENSION );
        } else {
          state = SCE_CONF_DEFAULT;
          chNext = styler[i--];
        }
        break;
      case SCE_CONF_STRING:
        if( ( ch == '"' && styler.SafeGetCharAt( i - 1 ) != '\\' ) || ( ch == '\n' ) || ( ch == '\r' ) ) {
          state = SCE_CONF_DEFAULT;
        }
        styler.ColourTo( i, SCE_CONF_STRING );
        break;
      case SCE_CONF_IDENTIFIER:
        if( ( IsASCII( ch ) && isalnum( ch ) ) || ( ch == '_' ) || ( ch == '-' ) || ( ch == '/' ) || ( ch == '$' ) || ( ch == '.' ) || ( ch == '*' ) ) {
          buffer[bufferCount++] = static_cast<char>( tolower( ch ) );
        } else {
          state = SCE_CONF_DEFAULT;
          buffer[bufferCount] = '\0';
          if( directives.InList( buffer ) ) {
            styler.ColourTo( i - 1, SCE_CONF_DIRECTIVE );
          } else if( params.InList( buffer ) ) {
            styler.ColourTo( i - 1, SCE_CONF_PARAMETER );
          } else if( strchr( buffer, '/' ) || strchr( buffer, '.' ) ) {
            styler.ColourTo( i - 1, SCE_CONF_EXTENSION );
          } else
          { styler.ColourTo( i - 1, SCE_CONF_DEFAULT ); }
          chNext = styler[i--];
        }
        break;
      case SCE_CONF_NUMBER:
        if( ( IsASCII( ch ) && isdigit( ch ) ) || ch == '.' ) {
          buffer[bufferCount++] = ch;
        } else {
          state = SCE_CONF_DEFAULT;
          buffer[bufferCount] = '\0';
          if( strchr( buffer, '.' ) ) {
            styler.ColourTo( i - 1, SCE_CONF_IP );
          } else
          { styler.ColourTo( i - 1, SCE_CONF_NUMBER ); }
          chNext = styler[i--];
        }
        break;
    }
  }
  delete []buffer;
}

static const char * const confWordListDesc[] = {
  "Directives",
  "Parameters",
  0
};

LexerModule lmConf( SCLEX_CONF, ColouriseConfDoc, "conf", 0, confWordListDesc );
